package com.binance.notification.web.service.abnormaltrade.log;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xxl.job.core.log.XxlJobFileAppender;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LoggerUtils {

    public static Logger getLogger(Class<?> clazz) {
        Logger logger = org.slf4j.LoggerFactory.getLogger(clazz);
        return new LoggerProxy(logger).getLogger();
    }

    // public static void main(String[] args) {
    // Logger logger = LoggerUtils.getLogger(LoggerUtils.class);
    // // logger.info("AbnormalPriceServiceImpl.monitorSymbolListAndStore symbolList={},asdfas={}",
    // // "aaa", "bbb");
    // logger.info("bb", "CC");
    // log.info("******************");
    // }

    public static class LoggerProxy implements InvocationHandler {
        private Logger logger;
        private Logger proxyLogger;

        public LoggerProxy(Logger logger) {
            this.logger = logger;
            this.proxyLogger = (Logger) Proxy.newProxyInstance(logger.getClass().getClassLoader(),
                    new Class[] {Logger.class}, this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object ret = method.invoke(this.logger, args);
            switch (method.getName()) {
                case "info":
                case "warn":
                case "error":
                    this.doXxljobLog(args);
            }
            return ret;
        }

        private void doXxljobLog(Object[] args) {
            StackTraceElement[] callInfo = new Throwable().getStackTrace();
            try {
                if (args != null && args.length > 0) {
                    Object arg = args[0];
                    if (arg instanceof String) {
                        String strArg = arg.toString();
                        if (args.length > 1) {
                            if (StringUtils.contains(strArg, "{}")) {
                                int index = 0;
                                while (StringUtils.contains(strArg, "{}")) {
                                    strArg = StringUtils.replaceOnce(strArg, "{}", "{" + (index++) + "}");
                                }
                                XxlJobLogger.log(strArg, ArrayUtils.subarray(args, 1, args.length));
                            } else {
                                for (int i = 1; i < args.length; i++) {
                                    strArg += "{" + (i - 1) + "},";
                                }
                                strArg = strArg.substring(0, strArg.length() - 1);
                                XxlJobLogger.log(strArg, ArrayUtils.subarray(args, 1, args.length));
                            }
                        } else {
                            XxlJobLogger.log(strArg);
                        }
                    } else if (arg instanceof Throwable) {
                        XxlJobLogger.log((Throwable) arg);
                    }
                }
            } catch (Exception e) {
                log.warn("打印日志出现异常", e);
            }
        }

        public Logger getLogger() {
            return this.proxyLogger;
        }
    }

    private static class XxlJobLogger {
        private static Logger logger = LoggerFactory.getLogger("xxl-job logger");
        private static SimpleDateFormat xxlJobLoggerFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // TODO，concurrent
                                                                                                          // issue

        /**
         * append log
         *
         * @param callInfo
         * @param appendLog
         */
        private static void logDetail(StackTraceElement callInfo, String appendLog) {


            /*
             * // "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
             * StackTraceElement[] stackTraceElements = new Throwable().getStackTrace();
             * StackTraceElement callInfo = stackTraceElements[1];
             */

            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(xxlJobLoggerFormat.format(new Date())).append(" ")
                    .append("[" + callInfo.getClassName() + "#" + callInfo.getMethodName() + "]").append("-")
                    .append("[" + callInfo.getLineNumber() + "]").append("-")
                    .append("[" + Thread.currentThread().getName() + "]").append(" ")
                    .append(appendLog != null ? appendLog : "");
            String formatAppendLog = stringBuffer.toString();

            // appendlog
            String logFileName = XxlJobFileAppender.contextHolder.get();
            if (logFileName != null && logFileName.trim().length() > 0) {
                XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
            } else {
                logger.info(">>>>>>>>>>> {}", formatAppendLog);
            }
        }

        /**
         * append log with pattern
         *
         * @param appendLogPattern like "aaa {0} bbb {1} ccc"
         * @param appendLogArguments like "111, true"
         */
        public static void log(String appendLogPattern, Object... appendLogArguments) {

            String appendLog = appendLogPattern;
            if (appendLogArguments != null && appendLogArguments.length > 0) {
                appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
            }

            StackTraceElement[] callInfos = new Throwable().getStackTrace();
            if (callInfos.length < 5) {
                logDetail(callInfos[1], appendLog);
            } else {
                logDetail(callInfos[4], appendLog);
            }
        }

        /**
         * append exception stack
         *
         * @param e
         */
        public static void log(Throwable e) {

            StringWriter stringWriter = new StringWriter();
            e.printStackTrace(new PrintWriter(stringWriter));
            String appendLog = stringWriter.toString();

            StackTraceElement[] callInfos = new Throwable().getStackTrace();
            if (callInfos.length < 5) {
                logDetail(callInfos[1], appendLog);
            } else {
                logDetail(callInfos[4], appendLog);
            }
        }

    }
}
